package com.xhj.mybatis.reflection.wrapper;

import com.xhj.mybatis.reflection.MetaClass;
import com.xhj.mybatis.reflection.MetaObject;
import com.xhj.mybatis.reflection.SystemMetaObject;
import com.xhj.mybatis.reflection.factory.ObjectFactory;
import com.xhj.mybatis.reflection.invoker.Invoker;
import com.xhj.mybatis.reflection.property.PropertyTokenizer;

import java.util.List;

/**
 * BeanWrapper
 *
 * @author XJks
 * @description BeanWrapper 类继承自 BaseWrapper，用于对 Java Bean 对象进行属性的反射操作
 */
public class BeanWrapper extends BaseWrapper {

    /**
     * 被包装的目标对象（Java Bean 实例）
     */
    private final Object object;

    /**
     * 用于描述目标对象的元类信息（如属性、getter/setter等）
     */
    private final MetaClass metaClass;

    /**
     * 构造方法，接收 MetaObject 和目标对象
     *
     * @param metaObject 反射操作的元对象
     * @param object     需要被包装的目标对象
     */
    public BeanWrapper(MetaObject metaObject, Object object) {
        // 调用父类构造方法
        super(metaObject);
        // 保存目标对象
        this.object = object;
        // 通过 MetaClass 工厂方法获取目标对象的元类信息
        this.metaClass = MetaClass.forClass(object.getClass(), metaObject.getReflectorFactory());
    }

    /**
     * 获取属性值
     *
     * @param prop 属性分词器，支持多级属性（如 user.address.street）
     * @return 属性的值
     */
    @Override
    public Object get(PropertyTokenizer prop) {
        // 如果属性有索引（如 list[0]），说明是集合类型
        if (prop.getIndex() != null) {
            // 解析集合对象
            Object collection = resolveCollection(prop, object);
            // 获取集合中的值
            return getCollectionValue(prop, collection);
        } else {
            // 普通属性，直接获取
            return getBeanProperty(prop, object);
        }
    }

    /**
     * 设置属性值
     *
     * @param prop  属性分词器
     * @param value 要设置的值
     */
    @Override
    public void set(PropertyTokenizer prop, Object value) {
        // 如果属性有索引，说明是集合类型
        if (prop.getIndex() != null) {
            // 解析集合对象
            Object collection = resolveCollection(prop, object);
            // 设置集合中的值
            setCollectionValue(prop, collection, value);
        } else {
            // 普通属性，直接设置
            setBeanProperty(prop, object, value);
        }
    }

    /**
     * 查找属性名，支持驼峰映射
     *
     * @param name                属性名
     * @param useCamelCaseMapping 是否使用驼峰命名映射
     * @return 实际属性名
     */
    @Override
    public String findProperty(String name, boolean useCamelCaseMapping) {
        return metaClass.findProperty(name, useCamelCaseMapping);
    }

    /**
     * 获取所有 getter 方法对应的属性名
     *
     * @return 属性名数组
     */
    @Override
    public String[] getGetterNames() {
        return metaClass.getGetterNames();
    }

    /**
     * 获取所有 setter 方法对应的属性名
     *
     * @return 属性名数组
     */
    @Override
    public String[] getSetterNames() {
        return metaClass.getSetterNames();
    }

    /**
     * 获取指定属性的 setter 方法参数类型
     *
     * @param name 属性名
     * @return setter 参数类型
     */
    @Override
    public Class<?> getSetterType(String name) {
        // 解析属性名
        PropertyTokenizer prop = new PropertyTokenizer(name);
        if (prop.hasNext()) {
            // 如果有子属性（如 a.b.c）
            MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());
            if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
                // 如果子属性为 null，直接用 metaClass 获取类型
                return metaClass.getSetterType(name);
            } else {
                // 递归获取子属性的类型
                return metaValue.getSetterType(prop.getChildren());
            }
        } else {
            // 没有子属性，直接获取类型
            return metaClass.getSetterType(name);
        }
    }

    /**
     * 获取指定属性的 getter 方法返回类型
     *
     * @param name 属性名
     * @return getter 返回类型
     */
    @Override
    public Class<?> getGetterType(String name) {
        PropertyTokenizer prop = new PropertyTokenizer(name);
        if (prop.hasNext()) {
            MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());
            if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
                return metaClass.getGetterType(name);
            } else {
                return metaValue.getGetterType(prop.getChildren());
            }
        } else {
            return metaClass.getGetterType(name);
        }
    }

    /**
     * 判断是否有指定属性的 setter 方法
     *
     * @param name 属性名
     * @return 是否有 setter
     */
    @Override
    public boolean hasSetter(String name) {
        PropertyTokenizer prop = new PropertyTokenizer(name);
        if (prop.hasNext()) {
            if (metaClass.hasSetter(prop.getIndexedName())) {
                MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());
                if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
                    return metaClass.hasSetter(name);
                } else {
                    return metaValue.hasSetter(prop.getChildren());
                }
            } else {
                return false;
            }
        } else {
            return metaClass.hasSetter(name);
        }
    }

    /**
     * 判断是否有指定属性的 getter 方法
     *
     * @param name 属性名
     * @return 是否有 getter
     */
    @Override
    public boolean hasGetter(String name) {
        PropertyTokenizer prop = new PropertyTokenizer(name);
        if (prop.hasNext()) {
            if (metaClass.hasGetter(prop.getIndexedName())) {
                MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());
                if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
                    return metaClass.hasGetter(name);
                } else {
                    return metaValue.hasGetter(prop.getChildren());
                }
            } else {
                return false;
            }
        } else {
            return metaClass.hasGetter(name);
        }
    }

    /**
     * 实例化属性值（如属性为 null 时自动创建对象），并返回其 MetaObject
     *
     * @param name          属性名
     * @param prop          属性分词器
     * @param objectFactory 对象工厂，用于创建新实例
     * @return 封装后的 MetaObject
     */
    @Override
    public MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory) {
        MetaObject metaValue;
        // 获取属性类型
        Class<?> type = getSetterType(prop.getName());
        try {
            // 创建新对象
            Object newObject = objectFactory.create(type);
            // TODO:将新创建的对象包装为 MetaObject 对象
            metaValue = MetaObject.forObject(newObject, metaObject.getObjectFactory(), metaObject.getObjectWrapperFactory(), metaObject.getReflectorFactory());
            // 设置到目标对象中
            set(prop, newObject);
        } catch (Exception e) {
            // 创建失败抛出异常
            throw new RuntimeException("Cannot set value of property '" + name + "' because '" + name + "' is null and cannot be instantiated on instance of " + type.getName() + ". Cause:" + e.toString(), e);
        }
        return metaValue;
    }

    /**
     * 通过反射获取 Java Bean 的属性值
     *
     * @param prop   属性分词器
     * @param object 目标对象
     * @return 属性值
     */
    private Object getBeanProperty(PropertyTokenizer prop, Object object) {
        try {
            // 获取 getter 方法
            Invoker method = metaClass.getGetInvoker(prop.getName());
            try {
                // 调用 getter 方法
                return method.invoke(object, NO_ARGUMENTS);
            } catch (Throwable t) {
                throw new RuntimeException(t);
            }
        } catch (RuntimeException e) {
            // 运行时异常直接抛出
            throw e;
        } catch (Throwable t) {
            // 其他异常包装成 RuntimeException 抛出
            throw new RuntimeException("Could not get property '" + prop.getName() + "' from " + object.getClass() + ".  Cause: " + t.toString(), t);
        }
    }

    /**
     * 通过反射设置 Java Bean 的属性值
     *
     * @param prop   属性分词器
     * @param object 目标对象
     * @param value  要设置的值
     */
    private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) {
        try {
            // 获取 setter 方法
            Invoker method = metaClass.getSetInvoker(prop.getName());
            // 参数数组
            Object[] params = {value};
            try {
                // 调用 setter 方法
                method.invoke(object, params);
            } catch (Throwable t) {
                throw new RuntimeException(t);
            }
        } catch (Throwable t) {
            // 异常包装成 RuntimeException 抛出
            throw new RuntimeException("Could not set property '" + prop.getName() + "' of '" + object.getClass() + "' with value '" + value + "' Cause: " + t.toString(), t);
        }
    }

    /**
     * 判断当前对象是否为集合类型，这里始终返回 false
     *
     * @return false
     */
    @Override
    public boolean isCollection() {
        return false;
    }

    /**
     * 向集合添加元素，这里不支持，直接抛异常
     *
     * @param element 要添加的元素
     */
    @Override
    public void add(Object element) {
        throw new UnsupportedOperationException();
    }

    /**
     * 向集合批量添加元素，这里不支持，直接抛异常
     *
     * @param list 要添加的元素列表
     * @param <E>  元素类型
     */
    @Override
    public <E> void addAll(List<E> list) {
        throw new UnsupportedOperationException();
    }

}
